package com.activenetwork.custom.uielement.decorate;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.util.List;

import org.openqa.selenium.WebElement;
import org.openqa.selenium.internal.Locatable;
import org.openqa.selenium.internal.WrapsElement;
import org.openqa.selenium.support.pagefactory.ElementLocator;
import org.openqa.selenium.support.pagefactory.ElementLocatorFactory;
import org.openqa.selenium.support.pagefactory.FieldDecorator;
import org.openqa.selenium.support.pagefactory.internal.LocatingElementHandler;
import org.openqa.selenium.support.pagefactory.internal.LocatingElementListHandler;

import com.activenetwork.custom.PageFactoryUtil;
import com.activenetwork.custom.uielement.UIElement;
import com.activenetwork.custom.uielement.XElement;

public class XElementDecorator implements FieldDecorator {

	private ElementLocatorFactory factory;
	private int sortOrder = 0;
	
	/**
	 * constructor
	 * @param factory
	 */
	public XElementDecorator(ElementLocatorFactory factory) {
		this.factory = factory;
	}
	
	public int getDecoratorOrder(){
		return sortOrder;
	}
	

	@Override
	public Object decorate(ClassLoader loader, Field field) {
				
		if (!WebElement.class.isAssignableFrom(field.getType())) { 
			if (!PageFactoryUtil.isDecoratableList(field)) {
				return null;
			}
		}

		ElementLocator locator = factory.createLocator(field);
		if (locator == null) {
			return null;
		}

		if (WebElement.class.isAssignableFrom(field.getType())) {
			return proxyForLocator(loader, locator, field);
		} else if (List.class.isAssignableFrom(field.getType())) {
			return proxyForListLocator(loader, locator, field);
		} else {
			return null;
		}

	}

	
	/**
	 * dynamic proxy to initialize the field which is UIElement type
	 * @param loader
	 * @param locator
	 * @return
	 */
	protected Object proxyForLocator(ClassLoader loader, ElementLocator locator, Field field) {
		InvocationHandler handler = new LocatingElementHandler(locator);
		WebElement proxy;
		proxy = (WebElement) Proxy.newProxyInstance(
				loader, 
				new Class[] {WebElement.class, WrapsElement.class, Locatable.class },
				handler);
		
		if(field.getType().equals(UIElement.class)){
			return new XElement(proxy);
		}else if(field.getType().equals(WebElement.class)){
			return proxy;
		}else{
			return null;
		}
	}
	/**
	 * dynamic proxy to initialize the field which is List<UIELement> type
	 * @param loader
	 * @param locator
	 * @return
	 */
	@SuppressWarnings("unchecked")
	protected Object proxyForListLocator(ClassLoader loader,ElementLocator locator, Field field) {
		InvocationHandler handler = new LocatingElementListHandler(locator);
		List<WebElement> proxy;
		proxy = (List<WebElement>) Proxy.newProxyInstance(
				loader,
				new Class[] { List.class }, 
				handler);
		
		ParameterizedType pt = (ParameterizedType)field.getGenericType();
		//String parameterName = pt.getActualTypeArguments()[0].toString();
		
		Type[] types = pt.getActualTypeArguments();
		Type type = types[0];
		
		if(type.equals(UIElement.class)){
			return (List<UIElement>)PageFactoryUtil.createCastValueList(proxy, XElement.class);
		}else if(type.equals(WebElement.class)){
			return proxy;
		}else{
			return null;
		}
	}

}
